package gloader

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/rpcxio/libkv"
	"github.com/rpcxio/libkv/store"
	eclient "github.com/rpcxio/rpcx-etcd/client"
	estore "github.com/rpcxio/rpcx-etcd/store"
	etcdV3 "github.com/rpcxio/rpcx-etcd/store/etcdv3"
	"github.com/smallnest/rpcx/client"
	"github.com/sony/sonyflake"
	clientv3 "go.etcd.io/etcd/client/v3"
	"go.uber.org/zap"
	"gopkg.in/yaml.v3"
	"os"
	"path/filepath"
	"sync"
	"wzgames/internal/constant"
	"wzgames/internal/model"
	"wzgames/pkg/logger"
	"wzgames/pkg/remote/etcd"
	"wzgames/pkg/utils/byteutil"
	"wzgames/pkg/utils/dateutil"
	"wzgames/pkg/utils/stringutil"
)

// 系统配置数据管理

var (
	loader *GLoader
	oncel  = sync.Once{}
)

type GLoader struct {
	bootc      *model.GBoot // 启动配置
	etcdClient *etcd.EtcdClient
	etcdStore  store.Store
	// 分类别处理
	ndMgr   *CommManager
	gameMgr *GameManager
	// SonyflakeID
	sonyFlakeID *sonyflake.Sonyflake

	evtBus  *sync.Map
	started bool
}

func Res() *GLoader {
	if loader == nil {
		oncel.Do(func() {
			loader = &GLoader{
				evtBus: &sync.Map{},
			}
		})
	}

	return loader
}

func (ld *GLoader) Start(bootFile string, nodeId string) error {
	if ld.started {
		return nil
	}
	// 加载configs/boot.yaml 上传到服务器后路径根据需要修改
	err := ld.loadBootConfig(bootFile)
	if err != nil {
		return err
	}
	ld.started = true
	ld.bootc.SetNodeID(nodeId)          // 设置节点ID
	constant.InitEnv(ld.bootc.GetEnv()) // 设置ENV
	loader.ndMgr = newCommManager()
	loader.gameMgr = newGameManager()

	// init logger default file
	if ld.bootc.LogC != nil && ld.bootc.LogC.Writers != nil {
		logFilename := stringutil.ToString(ld.bootc.LogC.Writers.FileConf["filename"])
		ld.bootc.LogC.Writers.FileConf["filename"] = ld.bootc.GetNodeID() + "_" + logFilename
	}
	err = logger.InitLogger(ld.bootc.LogC, zap.AddCallerSkip(3))
	if err != nil {
		//logger.Error("err == ", zap.String("error", err.Error()))
		return err
	}
	// init ETCD
	err = ld.initEtcdClient()
	if err != nil {
		//logger.Error("err == ", zap.String("error", err.Error()))
		return err
	}
	return ld.loadNode()
}

func (ld *GLoader) loadBootConfig(path string) error {
	configPath, _ := filepath.Abs(path)
	ext := filepath.Ext(configPath)
	if configPath == "" || (ext != ".yaml" && ext != ".yml") {
		return fmt.Errorf("[GLoader] boot config file name %s suffix must be .yml or .yaml", path)
	}
	content, err := os.ReadFile(path)
	if err != nil {
		return fmt.Errorf("[GLoader] boot os.ReadFile file: %s, error: %v", path, err)
	}
	c := &model.GBoot{}
	err = yaml.Unmarshal(content, c)
	if err != nil {
		return fmt.Errorf("[GLoader] boot config file Unmarshal error: %v", err)
	}

	ld.bootc = c

	return nil
}

func (ld *GLoader) initEtcdClient() error {
	etcdServ := etcd.NewEtcdClient(ld.bootc.CConf())
	err := etcdServ.CheckClientKV()
	if err != nil {
		//logger.Error("err == ", zap.String("error", err.Error()))
		return err
	}
	ld.etcdClient = etcdServ

	return nil
}

// initServiceDiscovery for rpcx etcd discovery
func (ld *GLoader) initServiceDiscovery() error {
	discoveryConfig := &store.Config{
		Username: ld.bootc.CC.UserName,
		Password: ld.bootc.CC.Password,
	}
	kv, err := libkv.NewStore(estore.ETCDV3, ld.bootc.CC.Addrs, discoveryConfig)
	if err != nil {
		//logger.Error("err == ", zap.String("error", err.Error()))
		return err
	}
	if ev3, ok := kv.(*etcdV3.EtcdV3); ok {
		ev3.AllowKeyNotFound = true
	}
	ld.etcdStore = kv

	return nil
}

// loadNode 加载ETCD配置的节点启动数据
func (ld *GLoader) loadNode() error {
	ret, err := ld.etcdClient.GetRawValue(constant.CcNodesRoot + "/" + ld.bootc.GetNodeID())
	if err != nil {
		//logger.Error("err == ", zap.String("error", err.Error()))
		return err
	}
	nd := &model.Node{}
	err = json.Unmarshal(ret, nd)
	if err != nil {
		//logger.Error("err == ", zap.String("error", err.Error()))
		return err
	}
	if nd.Env != ld.bootc.GetEnv() || !CheckEnv(nd.Env) {
		logger.Error("[GLoader] load node fail on Env", zap.String("Node", ld.bootc.GetNodeID()), zap.Any("Set", ld.bootc.GetEnv()), zap.Any("reality", nd.Env))
		return errors.New("invalid node env")
	}
	ld.bootc.SetNode(nd)
	return ld.initServiceDiscovery()
}

// initSonyFlakeID SonyFlakeID的初始化，在需要的地方调用，只初始化一次即可
func (ld *GLoader) initSonyFlakeID() error {
	if ld.bootc.Node().NodeId >= 65535 {
		return errors.New("invalid machineID, greater than uint16")
	}
	ld.sonyFlakeID = sonyflake.NewSonyflake(sonyflake.Settings{
		StartTime: dateutil.RfcToTime("2023-5-20 00:00:00", true),
		MachineID: func() (uint16, error) {
			return uint16(ld.bootc.Node().NodeId), nil
		},
	})
	return nil
}

func (ld *GLoader) AddEvtBusListener(evt string, fn func(interface{})) {
	ld.evtBus.LoadOrStore(evt, fn)
}

func (ld *GLoader) RemoveEvtBusListener(evt string) {
	ld.evtBus.Delete(evt)
}

func (ld *GLoader) FireEvtBusListener(evt string, arg interface{}) {
	fn, ok := ld.evtBus.Load(evt)
	if ok {
		fn.(func(interface{}))(arg)
	}
}

func (ld *GLoader) Stop() {
	if !ld.started {
		return
	}
	ld.ndMgr.Destroy()
	ld.gameMgr.Destroy()

	if ld.etcdClient != nil {
		_, _ = ld.etcdClient.Revoke(ld.etcdClient.LeaseID())
		_ = ld.etcdClient.Close()
	}

	if ld.etcdStore != nil {
		ld.etcdStore.Close()
	}
}

func BootConfig() *model.GBoot {
	if loader != nil {
		return loader.bootc
	}

	return nil
}

func CStore() store.Store {
	return loader.etcdStore
}

func CClient() *etcd.EtcdClient {
	return loader.etcdClient
}

func AddEvtBusListener(evt string, fn func(interface{})) {
	Res().AddEvtBusListener(evt, fn)
}

func RemoveEvtBusListener(evt string) {
	Res().RemoveEvtBusListener(evt)
}

func FireEvtBus(evt string, arg interface{}) {
	Res().FireEvtBusListener(evt, arg)
}

// NewServiceDiscovery rpcx service
func NewServiceDiscovery(servicePath string) client.ServiceDiscovery {
	discovery, _ := eclient.NewEtcdV3DiscoveryStore(constant.RPCxBasePath+"/"+servicePath, loader.etcdStore, true)
	return discovery
}

func RegisterNode(leaseID clientv3.LeaseID) error {
	if loader.bootc.Node().RpcConf != nil {
		b, err := json.Marshal(loader.bootc.Node().RpcConf)
		if err != nil {
			logger.Error("[GLoader] register node fail", zap.String("NodeID", loader.bootc.GetNodeID()), zap.Error(err))
			return err
		}
		if _, err = loader.etcdClient.Store(constant.CcNodeRpcRegPath+"/"+loader.bootc.GetNodeID(), byteutil.SliceByteToString(b), clientv3.WithLease(leaseID)); err != nil {
			//logger.Error("err == ", zap.String("error", err.Error()))
			return err
		}
	}

	return nil
}

func NodeLeaseChange(leaseID clientv3.LeaseID) error {
	if err := RegisterNode(leaseID); err != nil {
		//logger.Error("err == ", zap.String("error", err.Error()))
		return err
	}

	return nil
}

func GetAgent(agtId int64) *GAgent {
	return loader.gameMgr.LoadAgent(agtId)
}

func CommMgr() *CommManager {
	return loader.ndMgr
}

func GameMgr() *GameManager {
	return loader.gameMgr
}

func CheckEnv(env constant.EnvType) bool {
	switch env {
	case constant.EnvDev, constant.EnvDemo, constant.EnvProd:
		return true
	}

	return false
}

func EnvString() string {
	return string(loader.bootc.GetEnv())
}

// EnvHashAdjust 运行环境特殊处理，方便某些数据结构
func EnvHashAdjust() int64 {
	switch loader.bootc.GetEnv() {
	case constant.EnvDev:
		return 0
	case constant.EnvDemo:
		return 1
	case constant.EnvProd:
		return 2
	}
	return 0
}

// InitSonyFlakeID SonyFlakeID初始化
func InitSonyFlakeID() error {
	return loader.initSonyFlakeID()
}

// GenSonyFlakeID SonyFlakeID生成
func GenSonyFlakeID() uint64 {
	id, _ := loader.sonyFlakeID.NextID()
	return id
}
